use super::{const_buf, mut_buf, IoVec, IoVecMut};
use crate::{Error, Result};
use core::ptr;

/// 封装系统的文件句柄.
/// 所有数据收发(读写)在内部处理了EINTR错误码，使用者无需关心.
pub struct Fd {
    pub(crate) fd: i32,
}

impl Fd {
    /// 注意: fd的生命周期应该交给Fd管理，Fd析构的时候会关闭此句柄.
    pub fn new(fd: i32) -> Self {
        Self { fd }
    }

    /// 返回底层的句柄，同libc接口交互的时候需要.
    pub fn fd(&self) -> i32 {
        self.fd
    }

    /// 主动关闭句柄. 析构的时候会调用此接口.
    /// 关闭之后底层句柄变为-1，对系统而言是一个无效句柄.
    pub fn close(&mut self) {
        unsafe { libc::close(self.fd) };
        self.fd = -1;
    }

    /// 对应libc::read接口.
    pub fn try_read(&self, buf: &mut [u8]) -> Result<usize> {
        self.try_read_do(|| unsafe { libc::read(self.fd, mut_buf(buf), buf.len()) })
    }

    pub(crate) fn try_read_do<F: FnMut() -> isize>(&self, mut f: F) -> Result<usize> {
        loop {
            let ret = f();
            if ret >= 0 {
                return Ok(ret as usize);
            } else {
                let err = Error::last();
                match err.errno {
                    hierr::EINTR => continue,
                    _ => return Err(err),
                }
            }
        }
    }

    /// 对应libc::write接口.
    pub fn try_write(&self, buf: &[u8]) -> Result<usize> {
        self.try_write_do(|| unsafe { libc::write(self.fd, const_buf(buf), buf.len()) })
    }

    pub(crate) fn try_write_do<F: FnMut() -> isize>(&self, mut f: F) -> Result<usize> {
        loop {
            let ret = f();
            if ret >= 0 {
                return Ok(ret as usize);
            } else {
                let err = Error::last();
                match err.errno {
                    hierr::EINTR => continue,
                    _ => return Err(err),
                }
            }
        }
    }

    /// 对应libc::sendfile接口.
    pub fn try_sendfile(&self, in_fd: i32, off: usize, count: usize) -> Result<usize> {
        let mut off = off as i64;
        self.try_write_do(|| unsafe { libc::sendfile(self.fd, in_fd, &mut off, count) })
    }

    /// 对应libc::readv接口.
    /// off表示写入数据在buf中的起始偏移
    pub fn try_readv(&self, buf: &[&mut [u8]], off: usize) -> Result<usize> {
        self.try_readv_do(buf, off, |piovec, iovcnt| unsafe {
            libc::readv(self.fd, piovec, iovcnt)
        })
    }

    pub(crate) fn try_readv_do<F>(&self, buf: &[&mut [u8]], off: usize, mut f: F) -> Result<usize>
    where
        F: FnMut(*const libc::iovec, i32) -> isize,
    {
        let iovec = &mut [libc::iovec {
            iov_base: ptr::null_mut(),
            iov_len: 0,
        }; 64];
        let mut buf = IoVecMut::new(buf, off);
        let mut bytes = 0_usize;
        'top: while let Some((piovec, iovcnt, size)) = buf.to_iovec(iovec) {
            loop {
                let ret = f(piovec, iovcnt);
                if ret > 0 {
                    bytes += ret as usize;
                    if ret as usize == size {
                        buf = buf.next_iovec(iovcnt as usize);
                    } else {
                        buf = buf.next_bytes(ret as usize);
                    }
                    continue 'top;
                } else if ret == 0 {
                    break 'top;
                } else {
                    let errno = Error::last().errno;
                    match errno {
                        hierr::EAGAIN => break 'top,
                        hierr::EINTR => continue,
                        _ if bytes > 0 => break 'top,
                        _ => return Err(Error::new(errno)),
                    }
                }
            }
        }
        Ok(bytes)
    }

    /// 对应libc::writev接口.
    /// off表示发送数据在buf中的起始偏移
    pub fn try_writev(&self, buf: &[&[u8]], off: usize) -> Result<usize> {
        self.try_writev_do(buf, off, |piovec, iovcnt| unsafe {
            libc::writev(self.fd, piovec, iovcnt)
        })
    }

    pub(crate) fn try_writev_do<F>(&self, buf: &[&[u8]], off: usize, mut f: F) -> Result<usize>
    where
        F: FnMut(*const libc::iovec, i32) -> isize,
    {
        let iovec = &mut [libc::iovec {
            iov_base: ptr::null_mut(),
            iov_len: 0,
        }; 64];
        let mut buf = IoVec::new(buf, off);
        let mut bytes = 0_usize;
        'top: while let Some((piovec, iovcnt, size)) = buf.to_iovec(iovec) {
            loop {
                let ret = f(piovec, iovcnt);
                if ret > 0 {
                    bytes += ret as usize;
                    if ret as usize == size {
                        buf = buf.next_iovec(iovcnt as usize);
                    } else {
                        buf = buf.next_bytes(ret as usize);
                    }
                    continue 'top;
                } else {
                    let errno = Error::last().errno;
                    match errno {
                        hierr::EAGAIN => break 'top,
                        hierr::EINTR => continue,
                        _ if bytes > 0 => break 'top,
                        _ => return Err(Error::new(errno)),
                    }
                }
            }
        }
        Ok(bytes)
    }
}

/// 对应libc::dup功能.
impl Clone for Fd {
    fn clone(&self) -> Self {
        Self {
            fd: unsafe { libc::dup(self.fd) },
        }
    }
}

impl Drop for Fd {
    fn drop(&mut self) {
        unsafe { libc::close(self.fd) };
    }
}
